package com.dgut.util;

import io.jsonwebtoken.*;
import io.jsonwebtoken.security.Keys;
import io.jsonwebtoken.security.SignatureException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import com.dgut.Config.AppProperties;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.Key;
import java.util.Base64;
import java.util.Date;
import java.util.Optional;
import java.util.stream.Collectors;

@Component
public class JwtUtil {

    private final Key key;  //用于签名 Access Token
    private final Key refreshKey;   //用于签名 Refresh Token
    private final AppProperties appProperties;

    public JwtUtil(AppProperties appProperties) {
        this.appProperties = appProperties;
        System.out.println("============================");
        System.out.println(appProperties);
        System.out.println("============================");
        //System.out.println(appProperties.toString());
        //key = new SecretKeySpec(Base64.getDecoder().decode(
        //        appProperties.getJwt().getKey()), "HmacSHA512");
        refreshKey = new SecretKeySpec(Base64.getDecoder().decode(
                appProperties.getJwt().getKey()), "HmacSHA512");
        key = new SecretKeySpec(Base64.getDecoder().decode(
                appProperties.getJwt().getRefreshKey()), "HmacSHA512");
        //key = new SecretKeySpec(Base64.getDecoder().decode(appProperties.getJwt().getKey()),
        //        "HmacSHA512");
    }

    public String createJWTToken(UserDetails userDetails, long timeToExpire) {
        return createJWTToken(userDetails, timeToExpire, key);
    }

    public String createJWTToken(UserDetails userDetails, long timeToExpire, Key signKey){
        return Jwts
                .builder()
                .setSubject(userDetails.getUsername())
                .claim("authorities",
                        userDetails.getAuthorities().stream()
                                .map(GrantedAuthority::getAuthority)
                                .collect(Collectors.toList()))
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + timeToExpire))
                .signWith(signKey, SignatureAlgorithm.HS512).compact();
    }

    public String createAccessToken(UserDetails userDetails) {
        return createJWTToken(userDetails, appProperties.getJwt().getAccessTokenExpireTime());
    }

    public String createRefreshToken(UserDetails userDetails) {
        return createJWTToken(userDetails, appProperties.getJwt().getRefreshTokenExpireTime(), refreshKey);
    }

    public boolean validateAccessToken(String jwtToken){
        return validateToken(jwtToken, key);
    }

    public boolean validateRefreshToken(String jwtToken){
        return validateToken(jwtToken, refreshKey);
    }

    public boolean validateToken(String jwtToken, Key signKey) {
        System.out.println("Line 77: ");
        if(parseClaims(jwtToken, signKey) != null){
            return true;
        } else{
            return false;
        }
        //System.out.println("parseClaims(jwtToken, signKey).isPresent(): " + parseClaims(jwtToken, signKey).isPresent());
        //System.out.println("parseClaims(jwtToken, signKey): " + parseClaims(jwtToken, signKey));
        //if(parseClaims(parseClaims(jwtToken, signKey).isPresent()))
        //return parseClaims(jwtToken, signKey).isPresent();
    }

    public String buildAccessTokenWithRefreshToken(String jwtToken) throws Exception {
        return parseClaims(jwtToken, refreshKey)
                .map(claims -> Jwts.builder()
                        .setClaims(claims)
                        .setExpiration(new Date(System.currentTimeMillis() +
                                appProperties.getJwt().getAccessTokenExpireTime()))
                        .signWith(key, SignatureAlgorithm.HS512).compact())
                        .orElseThrow(()->new Exception());
        //return parseClaims(jwtToken, refreshKey)
        //        .map(claims -> Jwts.builder()
        //            .setClaims(claims)
        //            .setExpiration(new Date(System.currentTimeMillis() +
        //                    appProperties.getJwt().getAccessTokenExpireTime()))
        //            .signWith(key, SignatureAlgorithm.HS512).compact())
        //        .orElseThrow();
    }

    public Optional<Claims> parseClaims(String jwtToken, Key signKey) {

        try{
            Jws<Claims> parseClaimsJws = Jwts.parserBuilder().setSigningKey(signKey).build()
                    .parseClaimsJws(jwtToken);
            Claims body = parseClaimsJws.getBody();
            return Optional.ofNullable(body);
        } catch (SignatureException | MalformedJwtException e){
            return null;
        } catch (ExpiredJwtException e){
            return null;
        }
        //return Optional.ofNullable(Jwts.parserBuilder().setSigningKey(signKey).build()
        //.parseClaimsJws(jwtToken).getBody());
    }

    public boolean validateWithoutExpiration(String jwtToken) {
        try {
            Jwts.parserBuilder().setSigningKey(key).build().parseClaimsJws(jwtToken);
            System.out.println(parseClaims(jwtToken, key));
            return true;
        } catch (ExpiredJwtException | SignatureException |
                MalformedJwtException | UnsupportedJwtException | IllegalArgumentException e) {
            if(e instanceof ExpiredJwtException) {
                return true;
            }
        }
        return false;
    }

    public Key getKey(){
        return key;
    }

    public Key getRefreshKey() {
        return refreshKey;
    }

    public static void main(String[] args) throws UnsupportedEncodingException{
        SecretKey secretKey = Keys.secretKeyFor(SignatureAlgorithm.HS512);

        System.out.println(Base64.getEncoder().encodeToString(secretKey.getEncoded()));
    }

}
